<?php

declare(strict_types=1);

namespace App\Middleware;

use App\Constants\ErrorCode;
use App\Constants\Rbac\RouteCode;
use App\Constants\Redis\TempRedisCode;
use App\Exception\HeaderErrorException;
use App\Exception\RateLimitedException;
use App\Service\Rbac\RbacService;
use App\Utils\ContextUtil;
use App\Utils\Helper;
use App\Utils\JWT\Exceptions\TokenInvalidException;
use App\Utils\PrintLogger;
use App\Utils\RateLimiter;
use Hyperf\Collection\Arr;
use Hyperf\HttpMessage\Exception\HttpException;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Validation\Contract\ValidatorFactoryInterface;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

/**
 * 接口参数校验、限流
 */
class ApiMiddleware implements MiddlewareInterface
{
    public function __construct(protected ContainerInterface $container, RequestInterface $request)
    {
        $this->request = $request;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $path = $this->request->getPathInfo();
        PrintLogger::dump($path,'path');
        $pathInfo = \Hyperf\Support\make(RbacService::class)->getPathInfo($path);
        if (!$pathInfo){
            throw new HttpException(ErrorCode::NOT_FOUND);
        }
        ContextUtil::setPathInfo($pathInfo);
//        $this->auth($pathInfo);
        return $handler->handle($request);
    }

    protected function auth($pathInfo)
    {

        //限流
        if (!empty($pathInfo['rate_limit'])){
            $rate = explode(',',$pathInfo['rate_limit']['rate']);
            $by = $pathInfo['rate_limit']['by'];
            $key = '';
            switch ($by){
                case RouteCode::RATE_LIMIT_BY_PATH_UID:
                    if (ContextUtil::getUserId()){
                        $key = $pathInfo['path'] . ':' . ContextUtil::getUserId();
                        break;
                    }
                case RouteCode::RATE_LIMIT_BY_PATH_IP :
                    $key = $pathInfo['path'] . ':' . Helper::getRealIP($this->request);
                    break;
                case RouteCode::RATE_LIMIT_BY_PATH:
                default :
                    $key = $pathInfo['path'];
            }
            $rateLimiter = new RateLimiter(TempRedisCode::routeRateLimit($key));
            $rateLimiter->setLimit(intval($rate[0]));
            $rateLimiter->setLockSecond(intval($rate[1]));
            if ($rateLimiter->isLocked()){
                throw new RateLimitedException();
            }
            $rateLimiter->incrCnt();
        }

        // 是否校验头部字段
        if (!empty($pathInfo['header'])){
            $va = \Hyperf\Support\make(ValidatorFactoryInterface::class)
                ->make($this->request->getHeaders(),$pathInfo['header']);
            if ($va->fails()){
                throw new HeaderErrorException('请求参数设置错误');
            }
        }
        if (!empty($pathInfo['custom_rule'])){
            // 必须后台登录
            if ( Arr::hasAny($pathInfo['custom_rule'],RouteCode::mustAdminLogin()) ){
                if (empty(ContextUtil::getAdmin())){
                    throw new TokenInvalidException('token invalid');
                }
                // todo 后台权限校验

            }
            // 用户登录
            if ( Arr::hasAny($pathInfo['custom_rule'],RouteCode::mustUserLogin()) ){
                if (empty(ContextUtil::getUser())){
                    throw new TokenInvalidException('token invalid');
                }
                // todo 实名认证

            }

        }

    }


}
